package com.example.rpc.client;

import com.example.rpc.common.bean.RpcRequest;
import com.example.rpc.common.bean.RpcResponse;
import com.example.rpc.common.util.StringUtil;
import com.example.rpc.registry.ServiceDiscovery;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;

/**
 * @Author Anita
 * @Date 2017/12/25 18:25
 */
public class RpcProxy {
    private static final Logger LOGGER = LoggerFactory.getLogger(RpcProxy.class);

    private String serviceAddress;

    private ServiceDiscovery serviceDiscovery;

    public RpcProxy(ServiceDiscovery serviceDiscovery){
        this.serviceDiscovery = serviceDiscovery;
    }

    public <T> T create(final Class<?> interfaceClass){
        return  create(interfaceClass,"");
    }

    public <T> T create(final Class<?> interfaceClass,final String serviceVersion ){
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                                          new Class<?>[]{interfaceClass},
                                          new InvocationHandler() {
                                              @Override
                                              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                                  RpcRequest request = new RpcRequest();
                                                  request.setInterfaceName(method.getDeclaringClass().getName());
                                                  request.setRequestId(UUID.randomUUID().toString());
                                                  request.setMethodName(method.getName());
                                                  request.setServiceVersion(serviceVersion);
                                                  request.setParameterTypes(method.getParameterTypes());
                                                  request.setParameters(args);
                                                  //获取RPC服务地址
                                                  if(serviceDiscovery!=null){
                                                      String serviceName = interfaceClass.getName();
                                                      if(StringUtil.isNotEmpty(serviceVersion)){
                                                          serviceName+="-"+serviceVersion;
                                                      }
                                                      serviceAddress = serviceDiscovery.discover(serviceName);
                                                      LOGGER.debug("discover service: {} => {}", serviceName, serviceAddress);
                                                  }
                                                  if (StringUtil.isEmpty(serviceAddress)) {
                                                      throw new RuntimeException("server address is empty");
                                                  }
                                                  // 从 RPC 服务地址中解析主机名与端口号
                                                  String[] array = StringUtil.split(serviceAddress,":");
                                                  String host = array[0];
                                                  int port = Integer.parseInt(array[1]);
                                                  //创建RPC客户端对象并发送RPC请求
                                                  RpcClient client = new RpcClient(host,port);
                                                  long time = System.currentTimeMillis();
                                                  RpcResponse response = client.send(request);
                                                  LOGGER.debug("time: {}ms", System.currentTimeMillis() - time);
                                                  if(response == null){
                                                      throw new RuntimeException("response is null");
                                                  }
                                                  if(response.hasException()){
                                                      throw response.getException();
                                                  }else{
                                                    return  response.getResult();
                                                  }
                                              }
                                          });
    }
}
